/*
 * Copyright (C) 2010 MoSync AB
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "TinyJSInterpreter.h"
#include <string>
#include <conprint.h>

using namespace std;

namespace MAInterpreter
{

/**
 * Global that holds the current color set for drawing.
 * TODO: Make this an instance variable in TinyJSInterpreter,
 * rather than a global variable.
 */
static int CurrentColor = 0xFFFFFF;

// ========== Implementation of primitives ==========

static void jsPrint(CScriptVar *v, void *userdata) {
	lprintfln("> %s\n", v->getParameter("text")->getString().c_str());
}

static void jsDump(CScriptVar *v, void *userdata) {
    CTinyJS *js = (CTinyJS*)userdata;
    js->root->trace(">  ");
}

static int getScreenWidth()
{
	MAExtent screenSize = maGetScrSize();
	return EXTENT_X(screenSize);
}

static int getScreenHeight()
{
	MAExtent screenSize = maGetScrSize();
	return EXTENT_Y(screenSize);
}

static void jsScreenWidth(CScriptVar *v, void *userdata)
{
	int width = getScreenWidth();
	v->getReturnVar()->setInt(width);
}

static void jsScreenHeight(CScriptVar *v, void *userdata)
{
	int height = getScreenHeight();
	v->getReturnVar()->setInt(height);
}

static void jsSetClockMillisecondInterval(CScriptVar *v, void *userdata)
{
	// Get argument.
	int milliseconds = v->getParameter("milliseconds")->getInt();

    // Set clock interval.
    ((AbstractInterpreter*) userdata)->setClockMillisecondInterval(milliseconds);
}

static void jsCanvasSetColor(CScriptVar *v, void *userdata)
{
	int red = v->getParameter("red")->getInt();
	int green = v->getParameter("green")->getInt();
	int blue = v->getParameter("blue")->getInt();

	int color = ( red << 16 ) | ( green << 8 ) | blue;

	maSetColor(color);

	CurrentColor = color;
}

static void jsCanvasFillRect(CScriptVar *v, void *userdata)
{
	int left = v->getParameter("left")->getInt();
	int top = v->getParameter("top")->getInt();
	int width = v->getParameter("width")->getInt();
	int height = v->getParameter("height")->getInt();

	maFillRect(left, top, width, height);

	maUpdateScreen();
}

// ========== Constructor/Destructor ==========

TinyJSInterpreter::TinyJSInterpreter()
{
}

TinyJSInterpreter::~TinyJSInterpreter()
{
}

// ========== Methods ==========

#define REGFUN(FUNNAME, FUNPOINTER) if (1) { interpreter.addNative(FUNNAME, FUNPOINTER, this); }

int TinyJSInterpreter::initialize()
{
	// Add the functions from TinyJS_Functions.cpp
	registerFunctions(&interpreter);

	// Add a native functions.
	REGFUN("function print(text)", jsPrint);
	REGFUN("function dump(text)", jsDump);
	REGFUN("function ScreenWidth()", jsScreenWidth);
	REGFUN("function ScreenWidth()", jsScreenWidth);
	REGFUN("function ScreenHeight()", jsScreenHeight);
	REGFUN("function SetClockInterval(milliseconds)", jsSetClockMillisecondInterval);
	REGFUN("function SetColor(red,green,blue)", jsCanvasSetColor);
	REGFUN("function FillRect(left,top,width,height)", jsCanvasFillRect);

	return 1;
}

void TinyJSInterpreter::close()
{
}

int TinyJSInterpreter::runScript(const char* script)
{
	interpreter.execute(script);
	return 1;
}

void TinyJSInterpreter::onInit()
{
	callFunctionWithNumericArguments("OnInit", 0, 0, 0);
}

void TinyJSInterpreter::onDraw()
{
	callFunctionWithNumericArguments("OnDraw", 0, 0, 0);
}

void TinyJSInterpreter::onClockTick()
{
	callFunctionWithNumericArguments("OnClockTick", 0, 0, 0);
}

void TinyJSInterpreter::onTouchDown(int x, int y)
{
	callFunctionWithNumericArguments("OnTouchDown", 2, x, y);
}

void TinyJSInterpreter::onTouchUp(int x, int y)
{
	callFunctionWithNumericArguments("OnTouchUp", 2, x, y);
}

void TinyJSInterpreter::onTouchDrag(int x, int y)
{
	callFunctionWithNumericArguments("OnTouchDrag", 2, x, y);
}

void TinyJSInterpreter::onKeyDown(int keyCode)
{
	callFunctionWithNumericArguments("OnKeyDown", 1, keyCode, 0);
}

void TinyJSInterpreter::onKeyUp(int keyCode)
{
	callFunctionWithNumericArguments("OnKeyUp", 1, keyCode, 0);
}

/**
 * Helper function that calls a named JS function with 0..2 numeric arguments.
 */
void TinyJSInterpreter::callFunctionWithNumericArguments(
	const char* funName,
	int numberOfArguments,
	int arg1,
	int arg2)
{
	char script[255];

	sprintf(script, "Print('TinyJSInterpreter::callFunctionWithNumericArguments: Wrong number of arguments');");

	if (numberOfArguments == 0)
	{
		sprintf(script, "if (%s !== undefined) { %s(); }", funName, funName);
	}

	if (numberOfArguments == 1)
	{
		sprintf(script, "if (%s !== undefined) { %s(%d); }", funName, funName, arg1);
	}

	if (numberOfArguments == 2)
	{
		//sprintf(script, "if (%s !== undefined) { Print('Running %s'); %s(%d, %d); }", funName, funName, funName, arg1, arg2);
		sprintf(script, "if (%s !== undefined) { %s(%d, %d); }", funName, funName, arg1, arg2);
	}

	interpreter.execute(script);
}

}

